package cn.yjh.server.protocol.http;

import cn.yjh.server.Decoder;
import cn.yjh.server.Encoder;
import cn.yjh.server.protocol.ProtocolHandler;
import cn.yjh.server.support.Constants;
import cn.yjh.server.support.HttpHeader;
import cn.yjh.server.support.HttpStatus;
import cn.yjh.server.support.Request;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import cn.yjh.server.support.Response;
import cn.yjh.utils.ByteBufferUtils;
import cn.yjh.utils.StringUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;




public class HttpProtocolHandler extends ProtocolHandler implements Encoder, Decoder {

    Logger logger = LoggerFactory.getLogger(HttpProtocolHandler.class);

    private int startIndex = 0,endIndex=0;

    private long contentLength = 0;

    private final int PARSE_HEADER = 1;

    private final int PARSE_PARAMS = 2;

    private final int PARSE_FINISHED = 3;

    private int status = PARSE_HEADER;


    public HttpProtocolHandler(Request request, Response response) {
        super(request,response);
    }

    @Override
    public Request buildRequest() {
        return null;
    }

    @Override
    public Response buildResponse() {
        return null;
    }

    @Override
    public void decode() {
        if(logger.isDebugEnabled()){
            logger.info("use {} ", this.getClass().getSimpleName());
        }
        doDecode();
    }

    private void doDecode() {
        ByteBuffer buffer = request.getInputBuffer();
        SocketChannel sc = request.getSocketChannle();
        logger.debug("parse http header");
        while (true){
            if(!buffer.hasRemaining()){
                ByteBufferUtils.resetByteBuffer(buffer,startIndex,endIndex);
                startIndex = endIndex = 0;
                try {
                    sc.read(buffer);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            buffer.flip();
            if(status == PARSE_HEADER){
                parseHeader();
            }
            if(status == PARSE_PARAMS){
                parseParams();
            }
            if(status == PARSE_FINISHED){
                break;
            }
        }
    }

    /**
     * 解析请求头
     */
    private void parseHeader() {
        ByteBuffer buffer = request.getInputBuffer();
        Map<String, String> header = request.getHeader();
        byte preChr,chr = 0;
        while (buffer.hasRemaining()){
            preChr = chr;
            chr = buffer.get();
            endIndex = buffer.position();
            if(preChr == Constants.CR && chr == Constants.LF){
                String key_value = new String(buffer.array(), startIndex, endIndex-startIndex-2, StandardCharsets.UTF_8);
                startIndex = endIndex;
                // 解析到空行的时候 key_value 长度为 0，说明请求头已经解析完毕跳出循环
                if(key_value.length()==0){
                    status = PARSE_PARAMS;
                    break;
                }
                String[] arr = key_value.split(": ");
                header.put(arr[0],arr[1]);
            }
        }
    }

    /**
     * 解析请求参数
     */
    private void parseParams() {
        URLDecoder decoder = new URLDecoder();
        // 匹配URL参数的正则
        String regex = "([^\\?^&]+)=([^\\?\\&]+)?";
        Pattern pattern = Pattern.compile(regex);
        Map<String, Object> params = request.getParams();
        ByteBuffer buffer = request.getInputBuffer();
        // 判断是否是 POST 请求
        if("POST".equals(request.getMethod())){
            String contentType = request.getHeader().get("Content-Type");
            if(contentLength == 0){
                contentLength = Long.parseLong(request.getHeader().get("Content-Length"));
            }
            byte preChr,chr = 0;
            // 普通 form 表单数据提交
            if(contentType.equals(ContentType.application_X_WWW_form_urlencoded.value)){
                while (buffer.hasRemaining() && contentLength>0){
                    chr = buffer.get();
                    endIndex = buffer.position();
                    contentLength --;
                    String key_value = null;
                    if(chr == Constants.AMPERS_AND){
                        key_value = new String(buffer.array(), startIndex, endIndex-startIndex-1, StandardCharsets.UTF_8);
                    }
                    if(contentLength == 0){
                        key_value = new String(buffer.array(), startIndex, endIndex-startIndex, StandardCharsets.UTF_8);
                    }
                    if(chr == Constants.AMPERS_AND || contentLength == 0 ){
                        startIndex = endIndex;
                        Matcher matcher = pattern.matcher(key_value);
                        while(matcher.find()){
                            String key = matcher.group(1);
                            String value = matcher.group(2);
                            if(StringUtils.isEmpty(value)){
                                value = null;
                            }
                            // 解决中文乱码
                            try {
                                value = decoder.decode(value, "UTF-8");
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                            }
                            params.put(key,value);
                        }
                    }
                }
            }else{
                // 带有文件上传的 form 表单数据
                int index = contentType.indexOf("=")+1;
                String boundary = contentType.substring(index);
                while (buffer.hasRemaining() && contentLength>0){
                    preChr = chr;
                    chr = buffer.get();
                    endIndex = buffer.position();
                    contentLength --;
                    if(preChr == Constants.CR && chr == Constants.LF){
                        String key_value = new String(buffer.array(), startIndex, endIndex-startIndex-2, StandardCharsets.UTF_8);
                        System.out.println(key_value);
                        startIndex = endIndex;
                    }
                }
            }
            if(contentLength == 0){
                status = PARSE_FINISHED;
                return;
            }else{
                contentLength +=endIndex-startIndex;
            }
        }else
        // 判断是否是 GET 请求
        if("GET".equals(request.getMethod())){
            String queryString = request.getUrl();
            Matcher matcher = pattern.matcher(queryString);
            int no = 0;
            while(matcher.find()){
                logger.debug("param NO."+(++no)+"："+matcher.group(0));
                try {
                    params.put(matcher.group(1),decoder.decode(matcher.group(2),"UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }
            status = PARSE_FINISHED;
        }else{
            status = PARSE_FINISHED;
        }

    }

    @Override
    public void encode(Object o) {
        ByteBuffer outBuffer = response.getOutBuffer();
        StringBuilder headerBuilder = new StringBuilder();
        outBuffer.clear();
        if("OPTIONS".equals(request.getMethod())){
            headerBuilder.append("HTTP/1.1 200 OK\r\n");
            headerBuilder.append("Date:"+new Date()+"\r\n");
            // 允许跨域访问
            headerBuilder.append("Access-Control-Allow-Origin: *\r\n");
            headerBuilder.append("Access-Control-Allow-Methods: POST, GET, OPTIONS\r\n");
            headerBuilder.append("Access-Control-Allow-Headers: Content-Type\r\n");
            headerBuilder.append("Content-Type:text/html;charset=UTF-8\r\n");
            headerBuilder.append("Content-Length: 0\r\n");
            headerBuilder.append("Keep-Alive: timeout=2, max=100\r\n");
            headerBuilder.append("\r\n");
            outBuffer.put(headerBuilder.toString().getBytes(StandardCharsets.UTF_8));
            return;
        }

        switch (response.getStatus()){
            case OK:
                processOK();
                break;
            case NOT_FOUND:
                processNotFound();
                break;
        }
    }

    private void processOK() {
        Map<String, String> header = response.getHeader();
        ByteBuffer outBuffer = response.getOutBuffer();
        String contentDisposition = header.get(HttpHeader.CONTENT_DISPOSITION.VALUE);
        // 判断是否文件下载
        if(StringUtils.isNotEmpty(contentDisposition)){
            // 判断是否在线显示还是以附件的形式下载
            if(contentDisposition.contains("inline")){
                URL resourceURL = response.getResourceURL();
                InputStream stream = null;
                int contentLength = 0;
                try {
                    stream = resourceURL.openStream();
                    contentLength = stream.available();
                    header.put(HttpHeader.CONTENT_LENGTH.VALUE, String.valueOf(contentLength));
                    String headerString = buildHeader(header);
                    outBuffer.put(headerString.getBytes(StandardCharsets.UTF_8));
                    byte[] bytes = new byte[contentLength];
                    stream.read(bytes);
                    outBuffer.put(bytes);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }else{

            }
        }
    }

    private void processNotFound() {
        Map<String, String> header = response.getHeader();
        ByteBuffer outBuffer = response.getOutBuffer();
        header.put(HttpHeader.CONTENT_TYPE.VALUE,"text/plain");
        byte[] bytes = response.getMsg().getBytes(StandardCharsets.UTF_8);
        header.put(HttpHeader.CONTENT_LENGTH.VALUE,String.valueOf(bytes.length));
        String headerString = buildHeader(header);
        outBuffer.put(headerString.getBytes(StandardCharsets.UTF_8));
        outBuffer.put(bytes);
    }

    private String buildHeader(Map<String, String> header) {
        StringBuilder headerBuilder = new StringBuilder();
        headerBuilder.append("HTTP/1.1").append(" ").append(response.getStatus().code).append(" ").append(response.getStatus().content).append("\r\n");
        Set<Map.Entry<String, String>> entries = header.entrySet();
        entries.forEach(entry->{
            headerBuilder.append(entry.getKey()).append(":").append(entry.getValue()).append("\r\n");
        });
        headerBuilder.append(HttpHeader.ACCESS_CONTROL_ALLOW_ORIGIN).append(":").append("*").append("\r\n");
        headerBuilder.append("\r\n");
        return headerBuilder.toString();
    }

    enum ContentType{
        application_X_WWW_form_urlencoded("application/x-www-form-urlencoded"),
        multipart_form_data("multipart/form-data");
        String value;
        ContentType(String value) {
            this.value = value;
        }
    }
}
